#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/i2c-gpio.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>

static char eeprom[512];
static int cnt = 0;

/* i2c transfer data: i2c传输数据，模拟eeprom */
static int i2c_bus_bit_xfer(struct i2c_adapter *i2c_adap,
		    struct i2c_msg msgs[], int num)
{
	int i, j;
	for(i = 0;i < num; i++){
		if(msgs[i].addr == 0x50){
			/* read data */
			if(msgs[i].flags & I2C_M_RD){
				printk("read data\n");
				for(j = 0; j < msgs[i].len ; j++){
					msgs[i].buf[j] = eeprom[cnt++];
					if(cnt == 512)
						cnt = 0;
				}
			}
			/* write data */
			else {
				printk("write data\n");
				cnt = msgs[i].buf[0];
				for(j = 1; j < msgs[i].len ; j++){
					eeprom[cnt++] = msgs[i].buf[j];
					if(cnt == 512)
						cnt = 0;
				}
			}
		}
		else{
			i = -1;   //返回一个负数，代表该地址上无设备
			break;
		}
	}
	return i;
}

/* support protocols: 支持的协议 */
static u32 i2c_bus_bit_func(struct i2c_adapter *adap)
{
	return I2C_FUNC_I2C | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL |
			   I2C_FUNC_SMBUS_READ_BLOCK_DATA |
			   I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
			   I2C_FUNC_PROTOCOL_MANGLING;

}

const struct i2c_algorithm i2c_bus_algo = {
	.master_xfer	= i2c_bus_bit_xfer,
	.functionality	= i2c_bus_bit_func,
};


static struct i2c_adapter *i2c_bus;

static int i2c_bus_probe(struct platform_device *pdev)
{
	memset(eeprom, 0, sizeof(eeprom));

	/* alloc i2c_adapter */
	i2c_bus = kzalloc(sizeof(*i2c_bus), GFP_KERNEL);
	if (!i2c_bus){
		printk("kzalloc failed\n");
		return -ENOMEM;
	}

	/* set i2c_adapter */
	i2c_bus->owner = THIS_MODULE;
	i2c_bus->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
	i2c_bus->nr = -1;    //让内核自动分配i2c总线号
	i2c_bus->algo = &i2c_bus_algo;

	snprintf(i2c_bus->name, sizeof(i2c_bus->name), "i2c-hc");

	/* register i2c_adapter */
	i2c_add_adapter(i2c_bus);

	printk("i2c_adapter probe\n");

	return 0;
}

static int i2c_bus_remove(struct platform_device *pdev)
{
	i2c_del_adapter(i2c_bus);

	return 0;
}


static const struct of_device_id i2c_bus_dt_ids[] = {
	{ .compatible = "hc,i2c-hc", },
	{ /* sentinel */ }
};

static struct platform_driver i2c_bus_driver = {
	.driver		= {
		.name	= "i2c-bus",
		.of_match_table	= of_match_ptr(i2c_bus_dt_ids),
	},
	.probe		= i2c_bus_probe,
	.remove		= i2c_bus_remove,
};


static int __init ap3216_i2c_bus_init(void)
{
	int res;
	res = platform_driver_register(&i2c_bus_driver);
	if(res)
		printk("platform_driver_register i2c_buy failed\n");
	return res;
}

static void __exit ap3216_i2c_bus_exit(void)
{
	platform_driver_unregister(&i2c_bus_driver);
}

module_init(ap3216_i2c_bus_init);
module_exit(ap3216_i2c_bus_exit);
MODULE_LICENSE("GPL");


